/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package http.rpc;

import com.sun.org.apache.xerces.internal.impl.dv.util.Base64;
import jas.http.request.HttpRequest;
import jas.http.request.HttpRequestFormatter;
import jas.http.response.HttpResponse;
import jas.http.response.ResponseCode;
import jas.http.response.StringResponseBody;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;

/**
 *
 * @author Asger
 */
public class RpcHttpFormatterImpl implements RpcHttpFormatter {

    public RpcRequest parseRequest(HttpRequest request) {
        String[] parts = request.getPath().split("[?&/]");
        String className = parts[0];
        String objectId = parts[1];
        String methodName = parts[2];
        String[] parameters = new String[parts.length - 3];
        for (int i=0; i<parameters.length; i++) {
            parameters[i] = parts[i + 3];
        }
        
        Object[] paramObjects = new Object[parameters.length];
        for (int i=0; i<parameters.length; i++) {
            try {
                byte[] bytes = Base64.decode(parameters[i]);
                ObjectInputStream st = new ObjectInputStream(new ByteArrayInputStream(bytes));
                
                paramObjects[i] = st.readObject();
                
            } catch (IOException ex) {
                throw new RuntimeException("Invalid RPC request");
            } catch (ClassNotFoundException ex) {
                throw new RuntimeException("Invalid RPC request: Class not found: " + ex.getMessage());
            }
        }
        
        return new RpcRequest(objectId, methodName, paramObjects);
    }

    public HttpResponse writeResponse(Object returnValue) {
        HttpResponse response = new HttpResponse();
        response.setCode(ResponseCode.OK);
        
        try {
            ByteArrayOutputStream buffer = new ByteArrayOutputStream();
            ObjectOutputStream stream = new ObjectOutputStream(buffer);
            stream.writeObject(returnValue);
            String encoded = Base64.encode(buffer.toByteArray());
            response.setBody(new StringResponseBody(encoded));
        } catch (IOException ex) {
            ex.printStackTrace();
        }
        
        return response;
    }
    
    public HttpResponse writeException(Exception e) {
        HttpResponse response = new HttpResponse();
        String body = "";
        response.setCode(ResponseCode.INTERNAL_SERVER_ERROR);
        body = e.getClass().getName() + " " + e.getMessage();
        response.setBody(new StringResponseBody(body));
        return response;
    }

    public HttpRequest writeRequest(String objectId, String methodName, Object[] params) {
        
        String[] paramStrings = new String[params.length];
        for (int i=0; i<params.length; i++) {
            try {
                ByteArrayOutputStream buffer = new ByteArrayOutputStream();
                ObjectOutputStream stream = new ObjectOutputStream(buffer);
                stream.writeObject(params[i]);
                paramStrings[i] = Base64.encode(buffer.toByteArray());
            } catch (IOException ex) {
                ex.printStackTrace();
            }
        }
        
        String uri = "nullclassname/" + objectId + "/" + methodName + "?";
        
        for (int i=0; i<params.length; i++) {
            if (i != 0) {
                uri += "&";
            }
            uri += paramStrings[i];
        }
        
        return HttpRequestFormatter.createGETRequest(uri);
    }
    
    
    private static Exception parseException(String body) {
        int splitAt = body.indexOf(" ");
        if (splitAt == -1)
            return null;
        
        String exceptionName = body.substring(0, splitAt);
        String message = body.substring(splitAt + 1);
        
        if (exceptionName.equals(NonRemoteMethodException.class.getName())) {
            return new NonRemoteMethodException(message);
        }
        return null;
    }

    public RpcResponse parseResponse(HttpResponse response) {
        String body = null;
        try {
            body = response.getBodyAsString();
        } catch (IOException ex) {
            throw new RuntimeException(ex.getMessage());
        }
        
        RpcResponse rpc = new RpcResponse();
        
        if (response.getCode() == ResponseCode.INTERNAL_SERVER_ERROR) {
            
            Exception ex = parseException(body);
            if (ex == null)
                ex = new RuntimeException("Server reported exception: " + body);
            
            rpc.setException(ex);
            return rpc;
        }
        
        try {
            byte[] bytes = Base64.decode(body);
            ByteArrayInputStream buffer2 = new ByteArrayInputStream(bytes);
            ObjectInputStream input = new ObjectInputStream(buffer2);
            Object result = input.readObject();
            rpc.setResponse(result);
            return rpc;
            
        } catch (IOException ex) {
            throw new RuntimeException(ex.getMessage());
            
        } catch (ClassNotFoundException ex) {
            throw new RuntimeException(ex.getMessage());
        }
        
    }
    
}
